{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<font color=\"red\">注</font>: 使用 tensorboard 可视化需要安装 tensorflow (TensorBoard依赖于tensorflow库，可以任意安装tensorflow的gpu/cpu版本)\n",
    "\n",
    "```shell\n",
    "pip install tensorflow-cpu\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-01-21T06:59:06.984071Z",
     "start_time": "2025-01-21T06:59:06.979527Z"
    },
    "execution": {
     "iopub.execute_input": "2025-02-02T15:57:42.335982Z",
     "iopub.status.busy": "2025-02-02T15:57:42.335484Z",
     "iopub.status.idle": "2025-02-02T15:57:48.717497Z",
     "shell.execute_reply": "2025-02-02T15:57:48.716654Z",
     "shell.execute_reply.started": "2025-02-02T15:57:42.335932Z"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/usr/local/lib/python3.10/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n",
      "  from .autonotebook import tqdm as notebook_tqdm\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "sys.version_info(major=3, minor=10, micro=14, releaselevel='final', serial=0)\n",
      "matplotlib 3.10.0\n",
      "numpy 1.26.4\n",
      "pandas 2.2.3\n",
      "sklearn 1.6.0\n",
      "torch 2.5.1+cu124\n",
      "cuda:0\n"
     ]
    }
   ],
   "source": [
    "import matplotlib as mpl\n",
    "import matplotlib.pyplot as plt\n",
    "%matplotlib inline\n",
    "import numpy as np\n",
    "import sklearn\n",
    "import pandas as pd\n",
    "import os\n",
    "import sys\n",
    "import time\n",
    "from tqdm.auto import tqdm\n",
    "import torch\n",
    "import torch.nn as nn\n",
    "import torch.nn.functional as F\n",
    "\n",
    "print(sys.version_info)\n",
    "for module in mpl, np, pd, sklearn, torch:\n",
    "    print(module.__name__, module.__version__)\n",
    "    \n",
    "device = torch.device(\"cuda:0\") if torch.cuda.is_available() else torch.device(\"cpu\")\n",
    "print(device)\n",
    "\n",
    "seed = 42\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 数据准备\n",
    "\n",
    "https://www.kaggle.com/competitions/cifar-10/data\n",
    "\n",
    "```shell\n",
    "\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "ExecutionIndicator": {
     "show": true
    },
    "execution": {
     "iopub.execute_input": "2025-02-02T15:57:48.719290Z",
     "iopub.status.busy": "2025-02-02T15:57:48.718809Z",
     "iopub.status.idle": "2025-02-02T15:57:48.854144Z",
     "shell.execute_reply": "2025-02-02T15:57:48.852922Z",
     "shell.execute_reply.started": "2025-02-02T15:57:48.719260Z"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "test  train\n"
     ]
    }
   ],
   "source": [
    "!ls competitions/cifar-10"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-01-21T06:59:07.983879Z",
     "start_time": "2025-01-21T06:59:07.006666Z"
    },
    "ExecutionIndicator": {
     "show": true
    },
    "execution": {
     "iopub.execute_input": "2025-02-02T15:57:58.314424Z",
     "iopub.status.busy": "2025-02-02T15:57:58.313863Z",
     "iopub.status.idle": "2025-02-02T15:58:01.479393Z",
     "shell.execute_reply": "2025-02-02T15:58:01.478634Z",
     "shell.execute_reply.started": "2025-02-02T15:57:58.314389Z"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[(PosixPath('competitions/cifar-10/train/1.png'), 'frog'),\n",
      " (PosixPath('competitions/cifar-10/train/2.png'), 'truck'),\n",
      " (PosixPath('competitions/cifar-10/train/3.png'), 'truck'),\n",
      " (PosixPath('competitions/cifar-10/train/4.png'), 'deer'),\n",
      " (PosixPath('competitions/cifar-10/train/5.png'), 'automobile')]\n",
      "[(PosixPath('competitions/cifar-10/test/1.png'), 'cat'),\n",
      " (PosixPath('competitions/cifar-10/test/2.png'), 'cat'),\n",
      " (PosixPath('competitions/cifar-10/test/3.png'), 'cat'),\n",
      " (PosixPath('competitions/cifar-10/test/4.png'), 'cat'),\n",
      " (PosixPath('competitions/cifar-10/test/5.png'), 'cat')]\n",
      "50000 300000\n"
     ]
    }
   ],
   "source": [
    "from pathlib import Path\n",
    "\n",
    "DATA_DIR = Path(\".\")\n",
    "DATA_DIR1 =Path(\"competitions/cifar-10/\")\n",
    "train_lables_file = DATA_DIR / \"trainLabels.csv\"\n",
    "test_csv_file = DATA_DIR / \"sampleSubmission.csv\" #测试集模板csv文件\n",
    "train_folder = DATA_DIR1 / \"train\"\n",
    "test_folder = DATA_DIR1 / \"test\"\n",
    "\n",
    "#所有的类别\n",
    "class_names = [\n",
    "    'airplane',\n",
    "    'automobile',\n",
    "    'bird',\n",
    "    'cat',\n",
    "    'deer',\n",
    "    'dog',\n",
    "    'frog',\n",
    "    'horse',\n",
    "    'ship',\n",
    "    'truck',\n",
    "]\n",
    "\n",
    "def parse_csv_file(filepath, folder):\n",
    "    \"\"\"Parses csv files into (filename(path), label) format\"\"\"\n",
    "    results = []\n",
    "    #读取所有行\n",
    "    with open(filepath, 'r') as f:\n",
    "#         lines = f.readlines()  为什么加[1:]，可以试这个\n",
    "        #第一行不需要，因为第一行是标签\n",
    "        lines = f.readlines()[1:] \n",
    "    for line in lines:#依次去取每一行\n",
    "        image_id, label_str = line.strip('\\n').split(',')\n",
    "        image_full_path = folder / f\"{image_id}.png\"\n",
    "        results.append((image_full_path, label_str)) #得到对应图片的路径和分类\n",
    "    return results\n",
    "\n",
    "#解析对应的文件夹\n",
    "train_labels_info = parse_csv_file(train_lables_file, train_folder)\n",
    "test_csv_info = parse_csv_file(test_csv_file, test_folder)\n",
    "#打印\n",
    "import pprint\n",
    "pprint.pprint(train_labels_info[0:5])\n",
    "pprint.pprint(test_csv_info[0:5])\n",
    "print(len(train_labels_info), len(test_csv_info))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-01-21T06:59:08.015550Z",
     "start_time": "2025-01-21T06:59:07.983879Z"
    },
    "execution": {
     "iopub.execute_input": "2025-02-02T15:58:03.219261Z",
     "iopub.status.busy": "2025-02-02T15:58:03.218771Z",
     "iopub.status.idle": "2025-02-02T15:58:03.321089Z",
     "shell.execute_reply": "2025-02-02T15:58:03.320351Z",
     "shell.execute_reply.started": "2025-02-02T15:58:03.219226Z"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "                            filepath       class\n",
      "0  competitions/cifar-10/train/1.png        frog\n",
      "1  competitions/cifar-10/train/2.png       truck\n",
      "2  competitions/cifar-10/train/3.png       truck\n",
      "3  competitions/cifar-10/train/4.png        deer\n",
      "4  competitions/cifar-10/train/5.png  automobile\n",
      "                                filepath       class\n",
      "0  competitions/cifar-10/train/45001.png       horse\n",
      "1  competitions/cifar-10/train/45002.png  automobile\n",
      "2  competitions/cifar-10/train/45003.png        deer\n",
      "3  competitions/cifar-10/train/45004.png  automobile\n",
      "4  competitions/cifar-10/train/45005.png    airplane\n",
      "                           filepath class\n",
      "0  competitions/cifar-10/test/1.png   cat\n",
      "1  competitions/cifar-10/test/2.png   cat\n",
      "2  competitions/cifar-10/test/3.png   cat\n",
      "3  competitions/cifar-10/test/4.png   cat\n",
      "4  competitions/cifar-10/test/5.png   cat\n"
     ]
    }
   ],
   "source": [
    "# train_df = pd.DataFrame(train_labels_info)\n",
    "train_df = pd.DataFrame(train_labels_info[0:45000])\n",
    "valid_df = pd.DataFrame(train_labels_info[45000:])\n",
    "test_df = pd.DataFrame(test_csv_info)\n",
    "\n",
    "train_df.columns = ['filepath', 'class']\n",
    "valid_df.columns = ['filepath', 'class']\n",
    "test_df.columns = ['filepath', 'class']\n",
    "\n",
    "print(train_df.head())\n",
    "print(valid_df.head())\n",
    "print(test_df.head())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-01-21T06:59:09.221951Z",
     "start_time": "2025-01-21T06:59:08.015550Z"
    },
    "execution": {
     "iopub.execute_input": "2025-02-02T15:58:05.763374Z",
     "iopub.status.busy": "2025-02-02T15:58:05.762875Z",
     "iopub.status.idle": "2025-02-02T15:58:08.262978Z",
     "shell.execute_reply": "2025-02-02T15:58:08.262150Z",
     "shell.execute_reply.started": "2025-02-02T15:58:05.763339Z"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "from PIL import Image\n",
    "from torch.utils.data import Dataset, DataLoader\n",
    "from torchvision import transforms\n",
    "\n",
    "class Cifar10Dataset(Dataset):\n",
    "    df_map = {\n",
    "        \"train\": train_df,\n",
    "        \"eval\": valid_df,\n",
    "        \"test\": test_df\n",
    "    }\n",
    "    label_to_idx = {label: idx for idx, label in enumerate(class_names)}\n",
    "    idx_to_label = {idx: label for idx, label in enumerate(class_names)}\n",
    "    def __init__(self, mode, transform=None):\n",
    "        self.df = self.df_map.get(mode, None)\n",
    "        if self.df is None:\n",
    "            raise ValueError(\"mode should be one of train, val, test, but got {}\".format(mode))\n",
    "\n",
    "        self.transform = transform\n",
    "        \n",
    "    def __getitem__(self, index):\n",
    "        img_path, label = self.df.iloc[index]\n",
    "        img = Image.open(img_path).convert('RGB')\n",
    "        # # img 转换为 channel first\n",
    "        # img = img.transpose((2, 0, 1))\n",
    "        # transform\n",
    "        img = self.transform(img)\n",
    "        # label 转换为 idx\n",
    "        label = self.label_to_idx[label]\n",
    "        return img, label\n",
    "    \n",
    "    def __len__(self):\n",
    "        return self.df.shape[0]\n",
    "    \n",
    "IMAGE_SIZE = 32\n",
    "mean, std = [0.4914, 0.4822, 0.4465], [0.247, 0.243, 0.261]\n",
    "\n",
    "transforms_train = transforms.Compose([\n",
    "        # resize\n",
    "        transforms.Resize((IMAGE_SIZE, IMAGE_SIZE)),\n",
    "        # random rotation 40\n",
    "        transforms.RandomRotation(40),\n",
    "        # horizaontal flip\n",
    "        transforms.RandomHorizontalFlip(),\n",
    "        transforms.ToTensor(),\n",
    "        transforms.Normalize(mean, std)\n",
    "    ])\n",
    "\n",
    "transforms_eval = transforms.Compose([\n",
    "        # resize\n",
    "        transforms.Resize((IMAGE_SIZE, IMAGE_SIZE)),\n",
    "        transforms.ToTensor(),\n",
    "        transforms.Normalize(mean, std)\n",
    "    ])\n",
    "\n",
    "train_ds = Cifar10Dataset(\"train\", transforms_train)\n",
    "eval_ds = Cifar10Dataset(\"eval\", transforms_eval) "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-01-21T06:59:09.225457Z",
     "start_time": "2025-01-21T06:59:09.221951Z"
    },
    "execution": {
     "iopub.execute_input": "2025-02-02T15:58:10.881437Z",
     "iopub.status.busy": "2025-02-02T15:58:10.880759Z",
     "iopub.status.idle": "2025-02-02T15:58:10.886423Z",
     "shell.execute_reply": "2025-02-02T15:58:10.885500Z",
     "shell.execute_reply.started": "2025-02-02T15:58:10.881399Z"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "batch_size = 128\n",
    "train_dl = DataLoader(train_ds, batch_size=batch_size, shuffle=True, num_workers=4)   \n",
    "eval_dl = DataLoader(eval_ds, batch_size=batch_size, shuffle=False, num_workers=4)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-01-21T06:59:09.235243Z",
     "start_time": "2025-01-21T06:59:09.225966Z"
    },
    "execution": {
     "iopub.execute_input": "2025-01-21T07:49:18.957606Z",
     "iopub.status.busy": "2025-01-21T07:49:18.957295Z",
     "iopub.status.idle": "2025-01-21T07:49:18.959964Z",
     "shell.execute_reply": "2025-01-21T07:49:18.959495Z",
     "shell.execute_reply.started": "2025-01-21T07:49:18.957586Z"
    }
   },
   "outputs": [],
   "source": [
    "# 遍历train_ds得到每张图片，计算每个通道的均值和方差\n",
    "# def cal_mean_std(ds):\n",
    "#     mean = 0.\n",
    "#     std = 0.\n",
    "#     for img, _ in ds:\n",
    "#         mean += img.mean(dim=(1, 2))\n",
    "#         std += img.std(dim=(1, 2))\n",
    "#     mean /= len(ds)\n",
    "#     std /= len(ds)\n",
    "#     return mean, std\n",
    "#\n",
    "# # 经过 normalize 后 均值为0，方差为1\n",
    "# print(cal_mean_std(train_ds))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 定义模型"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-01-21T07:44:09.956439Z",
     "start_time": "2025-01-21T07:44:09.952969Z"
    },
    "ExecutionIndicator": {
     "show": true
    },
    "execution": {
     "iopub.execute_input": "2025-02-02T16:04:59.664658Z",
     "iopub.status.busy": "2025-02-02T16:04:59.664155Z",
     "iopub.status.idle": "2025-02-02T16:04:59.672861Z",
     "shell.execute_reply": "2025-02-02T16:04:59.672096Z",
     "shell.execute_reply.started": "2025-02-02T16:04:59.664604Z"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "class Resdiual(nn.Module):\n",
    "    \"\"\"浅层的残差块，无bottleneck（性能限制）\"\"\"\n",
    "    def __init__(self, input_channels, output_channels, use_1x1conv=False, stride=1):\n",
    "        \"\"\"\n",
    "        残差块\n",
    "        params filters: 过滤器数目，决定输出通道\n",
    "        params use_1x1conv: 是否使用 1x1 卷积，此时 stride=2，进行降采样\n",
    "        params strides: 步长，默认为1，当降采样的时候设置为2\n",
    "        \"\"\"\n",
    "        super().__init__()\n",
    "        self.conv1 = nn.Conv2d(\n",
    "            in_channels=input_channels,\n",
    "            out_channels=output_channels,\n",
    "            kernel_size=3,\n",
    "            stride=stride,\n",
    "            padding=1,\n",
    "        )\n",
    "        self.conv2 = nn.Conv2d(\n",
    "            in_channels=output_channels,\n",
    "            out_channels=output_channels,\n",
    "            kernel_size=3,\n",
    "            stride=1,\n",
    "            padding=1,\n",
    "        )  \n",
    "        if use_1x1conv:\n",
    "            # skip connection 的 1x1 卷积，用于改变通道数和降采样，使得最终可以做残差连接\n",
    "            self.conv_sc = nn.Conv2d(\n",
    "                in_channels=input_channels,\n",
    "                out_channels=output_channels,\n",
    "                kernel_size=1,\n",
    "                stride=stride,\n",
    "            )\n",
    "        else:\n",
    "            self.conv_sc = None\n",
    "        \n",
    "        self.bn1 = nn.BatchNorm2d(output_channels, eps=1e-5, momentum=0.9)\n",
    "        self.bn2 = nn.BatchNorm2d(output_channels, eps=1e-5, momentum=0.9)\n",
    "        \n",
    "    def forward(self, inputs):\n",
    "        \"\"\"forward\"\"\"\n",
    "        flow = F.relu(self.bn1(self.conv1(inputs))) #卷积->BN->ReLU\n",
    "        flow = self.bn2(self.conv2(flow)) #卷积->BN\n",
    "        # print(f'flow shape: {flow.shape}')\n",
    "        if self.conv_sc:#如果有1x1卷积，就用1x1卷积\n",
    "            inputs = self.conv_sc(inputs)\n",
    "        # print(f'inputs shape: {inputs.shape}')\n",
    "        return F.relu(flow + inputs) #残差连接->ReLU，必须保证flow和inputs的shape相同\n",
    "    \n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-01-21T07:44:13.122065Z",
     "start_time": "2025-01-21T07:44:13.118991Z"
    },
    "execution": {
     "iopub.execute_input": "2025-02-02T16:02:57.805948Z",
     "iopub.status.busy": "2025-02-02T16:02:57.805426Z",
     "iopub.status.idle": "2025-02-02T16:02:57.812180Z",
     "shell.execute_reply": "2025-02-02T16:02:57.811457Z",
     "shell.execute_reply.started": "2025-02-02T16:02:57.805911Z"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "class ResdiualBlock(nn.Module):\n",
    "    \"\"\"若干个 Resdiual 模块堆叠在一起，通常在第一个模块给 skip connection 使用 1x1conv with stride=2\"\"\"\n",
    "    def __init__(self, input_channels, output_channels, num, is_first=False):\n",
    "        \"\"\"\n",
    "        params filters: 过滤器数目\n",
    "        params num: 堆叠几个 Resdiual 模块\n",
    "        params is_first: 是不是第一个block。 最上面一层 Resdiual 的 stride=1,is_first=False,图像尺寸减半，False图像尺寸不变\n",
    "        \"\"\"\n",
    "        super().__init__()\n",
    "        self.model = nn.Sequential() # 用于存放 Resdiual 模块\n",
    "        self.model.append(Resdiual(\n",
    "            input_channels=input_channels, \n",
    "            output_channels=output_channels, \n",
    "            use_1x1conv=not is_first, \n",
    "            stride=1 if is_first else 2\n",
    "            )) # 第一个 Resdiual 模块，负责通道翻倍,图像的尺寸减半\n",
    "        for _ in range(1, num):\n",
    "            # 堆叠 num 个 Resdiual 模块\n",
    "            self.model.append(Resdiual(\n",
    "                input_channels=output_channels, \n",
    "                output_channels=output_channels,\n",
    "                use_1x1conv=False, stride=1\n",
    "                ))\n",
    "    def forward(self, inputs):\n",
    "        return self.model(inputs)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-01-21T07:44:20.834700Z",
     "start_time": "2025-01-21T07:44:20.831200Z"
    },
    "execution": {
     "iopub.execute_input": "2025-02-02T16:02:58.337985Z",
     "iopub.status.busy": "2025-02-02T16:02:58.337469Z",
     "iopub.status.idle": "2025-02-02T16:02:58.347164Z",
     "shell.execute_reply": "2025-02-02T16:02:58.346362Z",
     "shell.execute_reply.started": "2025-02-02T16:02:58.337950Z"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "class ResNetForCifar10(nn.Module):\n",
    "    def __init__(self, n=3, num_classes=10):\n",
    "        \"\"\"\n",
    "        params units: 预测类别的数目\n",
    "        \"\"\"\n",
    "        super().__init__()\n",
    "        self.model = nn.Sequential(\n",
    "            nn.Conv2d(\n",
    "                in_channels=3,\n",
    "                out_channels=16,\n",
    "                kernel_size=3,\n",
    "                stride=1,\n",
    "            ),  # conv1\n",
    "            nn.BatchNorm2d(16, momentum=0.9, eps=1e-5),\n",
    "            nn.ReLU(),\n",
    "            ResdiualBlock(input_channels=16, output_channels=16, num=2*n, is_first=True),  # conv2_x\n",
    "            ResdiualBlock(input_channels=16, output_channels=32, num=2*n),  # conv3_x\n",
    "            ResdiualBlock(input_channels=32, output_channels=64, num=2*n),  # conv4_x\n",
    "            # average pool\n",
    "            nn.AdaptiveAvgPool2d((1, 1)), #无论输入图片大小，输出都是1x1，把width和height压缩为1\n",
    "            nn.Flatten(),\n",
    "            # fully connected\n",
    "            nn.Linear(in_features=64, out_features=num_classes),\n",
    "            )\n",
    "        \n",
    "        self.init_weights()\n",
    "        \n",
    "    def init_weights(self):\n",
    "        \"\"\"使用 kaiming 均匀分布来初始化全连接层、卷积层的权重 W\"\"\"\n",
    "        for m in self.modules():\n",
    "            if isinstance(m, (nn.Linear, nn.Conv2d)):\n",
    "                nn.init.kaiming_uniform_(m.weight)\n",
    "                nn.init.zeros_(m.bias)\n",
    "        \n",
    "    def forward(self, inputs):\n",
    "        return self.model(inputs)\n",
    "\n",
    "\n",
    "# for key, value in ResNetForCifar10(num_classes=len(class_names)).named_parameters():\n",
    "#     print(f\"{key:^40}paramerters num: {np.prod(value.shape)}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-01-21T07:42:15.755683Z",
     "start_time": "2025-01-21T07:42:15.746118Z"
    },
    "collapsed": false,
    "execution": {
     "iopub.execute_input": "2025-02-02T16:03:03.339895Z",
     "iopub.status.busy": "2025-02-02T16:03:03.339383Z",
     "iopub.status.idle": "2025-02-02T16:03:03.366349Z",
     "shell.execute_reply": "2025-02-02T16:03:03.365630Z",
     "shell.execute_reply.started": "2025-02-02T16:03:03.339861Z"
    },
    "jupyter": {
     "outputs_hidden": false
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Total trainable parameters: 565386\n"
     ]
    }
   ],
   "source": [
    "#模型总参数量\n",
    "total_params = sum(p.numel() for p in ResNetForCifar10(num_classes=len(class_names)).parameters() if p.requires_grad)\n",
    "print(f\"Total trainable parameters: {total_params}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-01-21T07:44:30.716019Z",
     "start_time": "2025-01-21T07:44:30.689319Z"
    },
    "execution": {
     "iopub.execute_input": "2025-02-02T16:03:04.254044Z",
     "iopub.status.busy": "2025-02-02T16:03:04.253527Z",
     "iopub.status.idle": "2025-02-02T16:03:04.291815Z",
     "shell.execute_reply": "2025-02-02T16:03:04.291076Z",
     "shell.execute_reply.started": "2025-02-02T16:03:04.254009Z"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "flow shape: torch.Size([1, 16, 30, 30])\n",
      "inputs shape: torch.Size([1, 16, 30, 30])\n",
      "flow shape: torch.Size([1, 16, 30, 30])\n",
      "inputs shape: torch.Size([1, 16, 30, 30])\n",
      "flow shape: torch.Size([1, 16, 30, 30])\n",
      "inputs shape: torch.Size([1, 16, 30, 30])\n",
      "flow shape: torch.Size([1, 16, 30, 30])\n",
      "inputs shape: torch.Size([1, 16, 30, 30])\n",
      "flow shape: torch.Size([1, 16, 30, 30])\n",
      "inputs shape: torch.Size([1, 16, 30, 30])\n",
      "flow shape: torch.Size([1, 16, 30, 30])\n",
      "inputs shape: torch.Size([1, 16, 30, 30])\n",
      "flow shape: torch.Size([1, 32, 15, 15])\n",
      "inputs shape: torch.Size([1, 32, 15, 15])\n",
      "flow shape: torch.Size([1, 32, 15, 15])\n",
      "inputs shape: torch.Size([1, 32, 15, 15])\n",
      "flow shape: torch.Size([1, 32, 15, 15])\n",
      "inputs shape: torch.Size([1, 32, 15, 15])\n",
      "flow shape: torch.Size([1, 32, 15, 15])\n",
      "inputs shape: torch.Size([1, 32, 15, 15])\n",
      "flow shape: torch.Size([1, 32, 15, 15])\n",
      "inputs shape: torch.Size([1, 32, 15, 15])\n",
      "flow shape: torch.Size([1, 32, 15, 15])\n",
      "inputs shape: torch.Size([1, 32, 15, 15])\n",
      "flow shape: torch.Size([1, 64, 8, 8])\n",
      "inputs shape: torch.Size([1, 64, 8, 8])\n",
      "flow shape: torch.Size([1, 64, 8, 8])\n",
      "inputs shape: torch.Size([1, 64, 8, 8])\n",
      "flow shape: torch.Size([1, 64, 8, 8])\n",
      "inputs shape: torch.Size([1, 64, 8, 8])\n",
      "flow shape: torch.Size([1, 64, 8, 8])\n",
      "inputs shape: torch.Size([1, 64, 8, 8])\n",
      "flow shape: torch.Size([1, 64, 8, 8])\n",
      "inputs shape: torch.Size([1, 64, 8, 8])\n",
      "flow shape: torch.Size([1, 64, 8, 8])\n",
      "inputs shape: torch.Size([1, 64, 8, 8])\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "torch.Size([1, 10])"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "#随机一个张量，喂给上面的model\n",
    "x = torch.randn(1, 3, 32, 32)\n",
    "model = ResNetForCifar10(num_classes=len(class_names))\n",
    "model(x).shape"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "tags": []
   },
   "source": [
    "## 训练\n",
    "\n",
    "pytorch的训练需要自行实现，包括\n",
    "1. 定义损失函数\n",
    "2. 定义优化器\n",
    "3. 定义训练步\n",
    "4. 训练"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2025-02-02T16:03:36.963174Z",
     "iopub.status.busy": "2025-02-02T16:03:36.962674Z",
     "iopub.status.idle": "2025-02-02T16:03:37.074080Z",
     "shell.execute_reply": "2025-02-02T16:03:37.073296Z",
     "shell.execute_reply.started": "2025-02-02T16:03:36.963140Z"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "from sklearn.metrics import accuracy_score\n",
    "\n",
    "@torch.no_grad()\n",
    "def evaluating(model, dataloader, loss_fct):\n",
    "    loss_list = []\n",
    "    pred_list = []\n",
    "    label_list = []\n",
    "    for datas, labels in dataloader:\n",
    "        datas = datas.to(device)\n",
    "        labels = labels.to(device)\n",
    "        # 前向计算\n",
    "        logits = model(datas)\n",
    "        loss = loss_fct(logits, labels)         # 验证集损失\n",
    "        loss_list.append(loss.item())\n",
    "        \n",
    "        preds = logits.argmax(axis=-1)    # 验证集预测\n",
    "        pred_list.extend(preds.cpu().numpy().tolist())\n",
    "        label_list.extend(labels.cpu().numpy().tolist())\n",
    "        \n",
    "    acc = accuracy_score(label_list, pred_list)\n",
    "    return np.mean(loss_list), acc\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "jp-MarkdownHeadingCollapsed": true,
    "tags": []
   },
   "source": [
    "### TensorBoard 可视化\n",
    "\n",
    "\n",
    "训练过程中可以使用如下命令启动tensorboard服务。\n",
    "\n",
    "```shell\n",
    "tensorboard \\\n",
    "    --logdir=runs \\     # log 存放路径\n",
    "    --host 0.0.0.0 \\    # ip\n",
    "    --port 8848         # 端口\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2025-02-02T16:03:43.963133Z",
     "iopub.status.busy": "2025-02-02T16:03:43.962208Z",
     "iopub.status.idle": "2025-02-02T16:03:44.186081Z",
     "shell.execute_reply": "2025-02-02T16:03:44.185293Z",
     "shell.execute_reply.started": "2025-02-02T16:03:43.963093Z"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "from torch.utils.tensorboard import SummaryWriter\n",
    "\n",
    "\n",
    "class TensorBoardCallback:\n",
    "    def __init__(self, log_dir, flush_secs=10):\n",
    "        \"\"\"\n",
    "        Args:\n",
    "            log_dir (str): dir to write log.\n",
    "            flush_secs (int, optional): write to dsk each flush_secs seconds. Defaults to 10.\n",
    "        \"\"\"\n",
    "        self.writer = SummaryWriter(log_dir=log_dir, flush_secs=flush_secs)\n",
    "\n",
    "    def draw_model(self, model, input_shape):\n",
    "        self.writer.add_graph(model, input_to_model=torch.randn(input_shape))\n",
    "        \n",
    "    def add_loss_scalars(self, step, loss, val_loss):\n",
    "        self.writer.add_scalars(\n",
    "            main_tag=\"training/loss\", \n",
    "            tag_scalar_dict={\"loss\": loss, \"val_loss\": val_loss},\n",
    "            global_step=step,\n",
    "            )\n",
    "        \n",
    "    def add_acc_scalars(self, step, acc, val_acc):\n",
    "        self.writer.add_scalars(\n",
    "            main_tag=\"training/accuracy\",\n",
    "            tag_scalar_dict={\"accuracy\": acc, \"val_accuracy\": val_acc},\n",
    "            global_step=step,\n",
    "        )\n",
    "        \n",
    "    def add_lr_scalars(self, step, learning_rate):\n",
    "        self.writer.add_scalars(\n",
    "            main_tag=\"training/learning_rate\",\n",
    "            tag_scalar_dict={\"learning_rate\": learning_rate},\n",
    "            global_step=step,\n",
    "            \n",
    "        )\n",
    "    \n",
    "    def __call__(self, step, **kwargs):\n",
    "        # add loss\n",
    "        loss = kwargs.pop(\"loss\", None)\n",
    "        val_loss = kwargs.pop(\"val_loss\", None)\n",
    "        if loss is not None and val_loss is not None:\n",
    "            self.add_loss_scalars(step, loss, val_loss)\n",
    "        # add acc\n",
    "        acc = kwargs.pop(\"acc\", None)\n",
    "        val_acc = kwargs.pop(\"val_acc\", None)\n",
    "        if acc is not None and val_acc is not None:\n",
    "            self.add_acc_scalars(step, acc, val_acc)\n",
    "        # add lr\n",
    "        learning_rate = kwargs.pop(\"lr\", None)\n",
    "        if learning_rate is not None:\n",
    "            self.add_lr_scalars(step, learning_rate)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "jp-MarkdownHeadingCollapsed": true,
    "tags": []
   },
   "source": [
    "### Save Best\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2025-02-02T16:03:48.061117Z",
     "iopub.status.busy": "2025-02-02T16:03:48.060442Z",
     "iopub.status.idle": "2025-02-02T16:03:48.068023Z",
     "shell.execute_reply": "2025-02-02T16:03:48.067267Z",
     "shell.execute_reply.started": "2025-02-02T16:03:48.061079Z"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "class SaveCheckpointsCallback:\n",
    "    def __init__(self, save_dir, save_step=5000, save_best_only=True):\n",
    "        \"\"\"\n",
    "        Save checkpoints each save_epoch epoch. \n",
    "        We save checkpoint by epoch in this implementation.\n",
    "        Usually, training scripts with pytorch evaluating model and save checkpoint by step.\n",
    "\n",
    "        Args:\n",
    "            save_dir (str): dir to save checkpoint\n",
    "            save_epoch (int, optional): the frequency to save checkpoint. Defaults to 1.\n",
    "            save_best_only (bool, optional): If True, only save the best model or save each model at every epoch.\n",
    "        \"\"\"\n",
    "        self.save_dir = save_dir\n",
    "        self.save_step = save_step\n",
    "        self.save_best_only = save_best_only\n",
    "        self.best_metrics = -1\n",
    "        \n",
    "        # mkdir\n",
    "        if not os.path.exists(self.save_dir):\n",
    "            os.mkdir(self.save_dir)\n",
    "        \n",
    "    def __call__(self, step, state_dict, metric=None):\n",
    "        if step % self.save_step > 0:\n",
    "            return\n",
    "        \n",
    "        if self.save_best_only:\n",
    "            assert metric is not None\n",
    "            if metric >= self.best_metrics:\n",
    "                # save checkpoints\n",
    "                torch.save(state_dict, os.path.join(self.save_dir, \"best.ckpt\"))\n",
    "                # update best metrics\n",
    "                self.best_metrics = metric\n",
    "        else:\n",
    "            torch.save(state_dict, os.path.join(self.save_dir, f\"{step}.ckpt\"))\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "tags": []
   },
   "source": [
    "### Early Stop"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2025-02-02T16:04:24.229197Z",
     "iopub.status.busy": "2025-02-02T16:04:24.228691Z",
     "iopub.status.idle": "2025-02-02T16:04:24.235209Z",
     "shell.execute_reply": "2025-02-02T16:04:24.234295Z",
     "shell.execute_reply.started": "2025-02-02T16:04:24.229164Z"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "class EarlyStopCallback:\n",
    "    def __init__(self, patience=5, min_delta=0.01):\n",
    "        \"\"\"\n",
    "\n",
    "        Args:\n",
    "            patience (int, optional): Number of epochs with no improvement after which training will be stopped.. Defaults to 5.\n",
    "            min_delta (float, optional): Minimum change in the monitored quantity to qualify as an improvement, i.e. an absolute \n",
    "                change of less than min_delta, will count as no improvement. Defaults to 0.01.\n",
    "        \"\"\"\n",
    "        self.patience = patience\n",
    "        self.min_delta = min_delta\n",
    "        self.best_metric = -1\n",
    "        self.counter = 0\n",
    "        \n",
    "    def __call__(self, metric):\n",
    "        if metric >= self.best_metric + self.min_delta:\n",
    "            # update best metric\n",
    "            self.best_metric = metric\n",
    "            # reset counter \n",
    "            self.counter = 0\n",
    "        else: \n",
    "            self.counter += 1\n",
    "            \n",
    "    @property\n",
    "    def early_stop(self):\n",
    "        return self.counter >= self.patience\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "tags": []
   },
   "source": [
    "## training"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {
    "ExecutionIndicator": {
     "show": true
    },
    "execution": {
     "iopub.execute_input": "2025-02-02T16:19:04.390011Z",
     "iopub.status.busy": "2025-02-02T16:19:04.389449Z",
     "iopub.status.idle": "2025-02-02T16:23:11.531590Z",
     "shell.execute_reply": "2025-02-02T16:23:11.530307Z",
     "shell.execute_reply.started": "2025-02-02T16:19:04.389974Z"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "100%|██████████| 7040/7040 [04:06<00:00, 28.59it/s, epoch=19]\n"
     ]
    }
   ],
   "source": [
    "# 训练\n",
    "def training(\n",
    "    model, \n",
    "    train_loader, \n",
    "    val_loader, \n",
    "    epoch, \n",
    "    loss_fct, \n",
    "    optimizer, \n",
    "    tensorboard_callback=None,\n",
    "    save_ckpt_callback=None,\n",
    "    early_stop_callback=None,\n",
    "    eval_step=500,\n",
    "    ):\n",
    "    record_dict = {\n",
    "        \"train\": [],\n",
    "        \"val\": []\n",
    "    }\n",
    "    \n",
    "    global_step = 0\n",
    "    model.train()\n",
    "    with tqdm(total=epoch * len(train_loader)) as pbar:\n",
    "        for epoch_id in range(epoch):\n",
    "            # training\n",
    "            for datas, labels in train_loader:\n",
    "                datas = datas.to(device)\n",
    "                labels = labels.to(device)\n",
    "                # 梯度清空\n",
    "                optimizer.zero_grad()\n",
    "                # 模型前向计算\n",
    "                logits = model(datas)\n",
    "                # 计算损失\n",
    "                loss = loss_fct(logits, labels)\n",
    "                # 梯度回传\n",
    "                loss.backward()\n",
    "                # 调整优化器，包括学习率的变动等\n",
    "                optimizer.step()\n",
    "                preds = logits.argmax(axis=-1)\n",
    "            \n",
    "                acc = accuracy_score(labels.cpu().numpy(), preds.cpu().numpy())    \n",
    "                loss = loss.cpu().item()\n",
    "                # record\n",
    "                \n",
    "                record_dict[\"train\"].append({\n",
    "                    \"loss\": loss, \"acc\": acc, \"step\": global_step\n",
    "                })\n",
    "                \n",
    "                # evaluating\n",
    "                if global_step % eval_step == 0:\n",
    "                    model.eval()\n",
    "                    val_loss, val_acc = evaluating(model, val_loader, loss_fct)\n",
    "                    record_dict[\"val\"].append({\n",
    "                        \"loss\": val_loss, \"acc\": val_acc, \"step\": global_step\n",
    "                    })\n",
    "                    model.train()\n",
    "                    \n",
    "                    # 1. 使用 tensorboard 可视化\n",
    "                    if tensorboard_callback is not None:\n",
    "                        tensorboard_callback(\n",
    "                            global_step, \n",
    "                            loss=loss, val_loss=val_loss,\n",
    "                            acc=acc, val_acc=val_acc,\n",
    "                            lr=optimizer.param_groups[0][\"lr\"],\n",
    "                            )\n",
    "                \n",
    "                    # 2. 保存模型权重 save model checkpoint\n",
    "                    if save_ckpt_callback is not None:\n",
    "                        save_ckpt_callback(global_step, model.state_dict(), metric=val_acc)\n",
    "\n",
    "                    # 3. 早停 Early Stop\n",
    "                    if early_stop_callback is not None:\n",
    "                        early_stop_callback(val_acc)\n",
    "                        if early_stop_callback.early_stop:\n",
    "                            print(f\"Early stop at epoch {epoch_id} / global_step {global_step}\")\n",
    "                            return record_dict\n",
    "                    \n",
    "                # udate step\n",
    "                global_step += 1\n",
    "                pbar.update(1)\n",
    "                pbar.set_postfix({\"epoch\": epoch_id})\n",
    "        \n",
    "    return record_dict\n",
    "        \n",
    "\n",
    "epoch = 20\n",
    "\n",
    "model = ResNetForCifar10(num_classes=10)\n",
    "\n",
    "# 1. 定义损失函数 采用交叉熵损失\n",
    "loss_fct = nn.CrossEntropyLoss()\n",
    "# 2. 定义优化器 采用 Rmsprop\n",
    "# Optimizers specified in the torch.optim package\n",
    "# >>> # Assuming optimizer uses lr = 0.05 for all groups\n",
    "# >>> # lr = 0.05     if epoch < 30\n",
    "# >>> # lr = 0.005    if 30 <= epoch < 80\n",
    "# >>> # lr = 0.0005   if epoch >= 80\n",
    "# >>> scheduler = MultiStepLR(optimizer, milestones=[30,80], gamma=0.1)\n",
    "\n",
    "class OptimizerWithScheduler:\n",
    "    def __init__(self, parameters, lr, momentum, weight_decay):\n",
    "        self.optimizer = torch.optim.SGD(parameters, lr=lr, momentum=momentum, weight_decay=weight_decay) # 优化器\n",
    "        self.scheduler = torch.optim.lr_scheduler.MultiStepLR(self.optimizer, milestones=[32_000, 48_000], gamma=0.1) # 学习率衰减\n",
    "        \n",
    "    def step(self):\n",
    "        self.optimizer.step()\n",
    "        self.scheduler.step()\n",
    "        \n",
    "    @property\n",
    "    def param_groups(self):\n",
    "        return self.optimizer.param_groups\n",
    "        \n",
    "    def zero_grad(self):\n",
    "        self.optimizer.zero_grad()\n",
    "        \n",
    "optimizer = OptimizerWithScheduler(model.parameters(), lr=0.1, momentum=0.9, weight_decay=1e-4)\n",
    "\n",
    "# 1. tensorboard 可视化\n",
    "if not os.path.exists(\"runs\"):\n",
    "    os.mkdir(\"runs\")\n",
    "    \n",
    "exp_name = \"resnet_cifar\"\n",
    "tensorboard_callback = TensorBoardCallback(f\"runs/{exp_name}\")\n",
    "tensorboard_callback.draw_model(model, [1, 3, IMAGE_SIZE, IMAGE_SIZE])\n",
    "# 2. save best\n",
    "if not os.path.exists(\"checkpoints\"):\n",
    "    os.makedirs(\"checkpoints\")\n",
    "save_ckpt_callback = SaveCheckpointsCallback(f\"checkpoints/{exp_name}\", save_step=len(train_dl), save_best_only=True)\n",
    "# 3. early stop\n",
    "early_stop_callback = EarlyStopCallback(patience=5)\n",
    "\n",
    "model = model.to(device)\n",
    "record = training(\n",
    "    model, \n",
    "    train_dl, \n",
    "    eval_dl, \n",
    "    epoch, \n",
    "    loss_fct, \n",
    "    optimizer, \n",
    "    tensorboard_callback=None,\n",
    "    save_ckpt_callback=save_ckpt_callback,\n",
    "    early_stop_callback=early_stop_callback,\n",
    "    eval_step=len(train_dl)\n",
    "    )"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2025-02-02T16:23:42.936418Z",
     "iopub.status.busy": "2025-02-02T16:23:42.935736Z",
     "iopub.status.idle": "2025-02-02T16:23:43.366573Z",
     "shell.execute_reply": "2025-02-02T16:23:43.365582Z",
     "shell.execute_reply.started": "2025-02-02T16:23:42.936355Z"
    },
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAy0AAAHACAYAAACvVYdzAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjAsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvlHJYcgAAAAlwSFlzAAAPYQAAD2EBqD+naQAA1SJJREFUeJzs3Xd4FOX2wPHvbMkmgYROaIHQu4A0AUWUpgh2RcWroGLlp1e8XsV7FbFhRezYsBe8ivdaECmKSBMEQUSK1NASQktI3TLz+2Ozm91sm002ZZLzeR4ektmZ2bObbHbOnve8r6JpmoYQQgghhBBCVFOmqg5ACCGEEEIIIcKRpEUIIYQQQghRrUnSIoQQQgghhKjWJGkRQgghhBBCVGuStAghhBBCCCGqNUlahBBCCCGEENWaJC1CCCGEEEKIak2SFiGEEEIIIUS1ZqnsO1RVlUOHDpGUlISiKJV990IIUWtpmsapU6do0aIFJpN8ZuUh70tCCFF19L43VXrScujQIVJTUyv7boUQQhTbv38/rVq1quowqg15XxJCiKoX6b2p0pOWpKQkwB1YcnJy1Mc7HA4WLVrEqFGjsFqtsQ6vQhgxZjBm3EaMGYwZtxFjBmPGHauYc3JySE1N9f4dFm618X0JjBm3EWMGibsyGTFmMGbclf3eVOlJi6f0npycXOY3h8TERJKTkw31QzVazGDMuI0YMxgzbiPGDMaMO9YxyxAof7XxfQmMGbcRYwaJuzIZMWYwZtyV/d4kg5qFEEIIIYQQ1ZokLUIIIYQQQohqTZIWIYQQQgghRLVW6T0tQojqSdM0nE4nLpdL1/4OhwOLxUJhYaHuY6oDI8atN2az2YzFYpGelQoQ7vVhxN8pMGbcZYlZXhdC1AyStAghsNvtHD58mPz8fN3HaJpGs2bN2L9/v6EuBowYdzQxJyYm0rx5c+Li4iopupov0uvDiL9TYMy4yxqzvC6EMD5JWoSo5VRVZc+ePZjNZlq0aEFcXJyuiwFVVcnNzaVu3bqGWqjQiHHriVnTNOx2O1lZWezZs4eOHTsa5vFVZ3peH0b8nQJjxh1tzPK6EKLmkKRFiFrObrejqiqpqakkJibqPk5VVex2O/Hx8Ya6CDBi3HpjTkhIwGq1sm/fPu/+onz0vD6M+DsFxoy7LDHL60KImsEYf6WEEBXOKBctIjz5OVYMeV6NTX5+QhifvIqFEEIIIYQQ1ZokLUIIIYQQQohqTZIWIYQA0tLSmD17dkzOtWzZMhRF4eTJkzE5nxBVLZavDyGEKAtpxBdCGNawYcPo3bt3TC6m1q1bR506dcoflBDVhLw+hBA1iSQtQogaS9M0XC4XFkvkP3VNmjSphIiEqD48C2bqIa8PIURVM9bwsE2fYnnzbLodnFfVkQhRY2maRr7dqetfgd2le189/zRN0x3nxIkT+emnn3jhhRdQFAVFUXj33XdRFIXvvvuOvn37YrPZWLFiBbt27eKiiy4iJSWF5ORkzj33XJYsWeJ3vtLDXxRF4a233uKSSy4hMTGRjh078tVXX5X5ef3iiy/o3r07NpuNtLQ0nnvuOb/bX331VTp27Eh8fDwpKSlcfvnl3ts+//xzBg8eTJ06dWjUqBEjRowgLy+vzLGI8gn2Gon1a6G8rxE9r4+EhATWrFnj9/qoW7cu/fv3r9DXh8vl4sYbb6Rt27YkJCTQuXNnXnjhhYD95s6d633NNG/enClTpnhvO3nyJLfccgspKSnEx8fTo0cPvvnmG133L4QocTi7gInvrGXZ9iNVHUpExqq0FOagHNlCYn39a0kIIaJT4HDR7aHvq+S+/3xkNIlx+v4svfDCC+zYsYMePXrwyCOPALBlyxYA7r//fp599lnatWtHgwYN2L9/P2PGjOHxxx/HarXy1ltvcdFFF7F9+3Zat24d8j5mzJjB008/zTPPPMNLL73EhAkT2LdvHw0bNozqca1fv54rr7yShx9+mPHjx7Nq1Spuv/12GjVqxMSJE/n111+58847+eCDDxg8eDDHjx/n559/BuDw4cNMmDCBGTNmcNVVV5GXl8fPP/8cVYInYssIrxE9r4+0tDQsFgsnT570vj5sNhvvv/8+48aNq7DXh6qqtGrViv/85z80atSIVatWcfPNN9O8eXOuvPJKAF577TWmTp3Kk08+yfnnn092djYrV670Hn/BBRdw6tQpPvzwQ9q3b8+ff/6J2WzW9RwKIUo8MH8zy7ZnsWx7FnufvKCqwwnLWElLfDIAVldBFQcihKhq9erVIy4ujsTERJo1awbAtm3bAHjkkUcYOXKkd9+GDRvSq1cvwH3B869//YvvvvuOr776yu/T29ImTpzI1VdfDcATTzzBiy++yNq1aznvvPOiinXWrFkMHz6cBx98EIBOnTrx559/8swzzzBx4kTS09OpU6cOY8eOJSkpiTZt2tCnTx/AnbQ4nU7Gjh1LWloaJpOJnj17RnX/ovbR8/pQVZWcnBy/3zeARx99lC+//LLCXh9Wq5UZM2Z4v2/bti2rV6/ms88+8yYtjz32GPfccw933XWXd7/+/fujqirLli1j7dq1bN26lU6dOgHQrl27aJ4eIUSxjJyiqg5BN2MlLbYkACyStAhRYRKsZv58ZHTE/VRV5VTOKZKSk2K2cFuCNTaflPbr18/v+9zcXB5++GG+/fZbbxJQUFBAenp62POcdtpp3q/r1KlDcnIyR45EX0LfunUrF110kd+2IUOGMHv2bFwuFyNHjqRNmza0a9eO8847j/POO8877KZXr14MHz6cM888k1GjRjF69Gguv/xyGjRoEHUcIjZKv0Yq4rUQ7r7LK9jr45FHHqnU18crr7zC3LlzSU9Pp6CgALvdTu/evQE4cuQIhw4dYvjw4UGP3bx5M61atfImLEKI2sFgSUtxpUWVpEWIiqIoiq7hJ6qq4owzkxhnqXarTZee5egf//gHixcv9g4Zc7lc3HDDDdjt9rDnsVqtft8rioKqqjGPNykpiQ0bNrBs2TIWLVrEQw89xMMPP8y6deuoX78+33//PYsXL2bVqlW89NJL/Otf/+KXX36hbdu2MY9FRFb6NVKdXwvBlH593HvvvSxZsoRnn32WDh06kJCQwOWXX15hr49PP/2Uf/zjHzz33HMMGjSIpKQknnnmGX755RcAEhISwh4f6XYhRM1U/f+6+pJKixDCR1xcHC6XK+J+K1euZOLEiVxyySX07NmTpk2bsnfv3ooPsFjXrl294/F9Y+rUqZN3HL7FYmHEiBE8/fTT/P777+zdu5cffvgBcF8MnnHGGTz88MP89ttvxMXF8eWXX1Za/MKY9L4+Vq1a5ff6aNasWYW+PlauXMngwYO5/fbb6dOnDx06dGDXrl3e25OSkkhLS2Pp0qVBj+/evTsHDhxgx44dFRajEKL6MValxaenRVpQhRBpaWn88ssv7N27l7p164b8lLdjx47Mnz+fcePGoWkaDzzwQIVUTEK555576N+/P48++ijjx49n9erVvPzyy7z66qsAfPPNN+zevZuhQ4fSoEEDFixYgKqqdO7cmV9++YUlS5YwePBg2rZty7p168jKyqJr166VFr8wJr2vjw4dOnhfH4qi8OCDD1bo66Njx468//77fP/997Rt25YPPviAdevW+VUOH374YW699VaaNm3K+eefz6lTp1i5ciV33HEHQ4YMYejQoVx22WXMmjWLDh06sG3bNhRFibrfTAhhHAartLiTFotaCGrkT4+EEDXbP/7xD8xmM926daNJkyYhx+DPmjWLBg0aMHjwYC666CLOPfdcTj/99EqL8/TTT+ezzz7j008/pUePHjz00EM88sgjTJw4EYD69eszf/58zj33XLp27cqcOXP45JNP6N69O8nJySxfvpwrr7ySLl268O9//5vnnnuO888/v9LiF8ak9/Xx3HPPeV8f48aNY/To0RX6+rjlllu49NJLGT9+PAMHDuTYsWPcfvvtfvtcf/31zJ49m1dffZXu3bszduxY/vrrL+/t//nPf+jfvz9XX3013bp145///KeuqpIQwriMVWkpHh4GgD0XbPFVF4sQosp16tSJ1atX+23zJAK+0tLSvEOtPDMm3XPPPX79B6WHwwSbUvjkyZO64ho2bFjA8ZdddhmXXXZZ0P3PPPNMli1bFvS2rl278t1335GTk0NycrIheiZE9VCW14fHHXfc4fd9LF8fNpuNd955h3feecdv+8yZM/2+v+WWW7jlllv8tnkqQA0bNmTu3Lm67k8IUTMY693PYkMz29xfF52q2liEEEIIIYQwMCOt+WWspAVKqi2StAghqsitt95K3bp1g/679dZbqzo8IaqUvD6EEBXBWMPDwJ205B9FKcqp6kiEELXUI488wj/+8Y+gtyUnJ1dyNEJUL/L6EEJUBGMmLSCVFiFElWnatClNmzat6jCEqJbk9SGEqAiGGx6mFc8ghlRahBBCCCGEqBUMl7RIpUUIIYQQQojaxbBJi/S0CCGEEEIIUTsYLmkpGR6WW7WBCCGEEEIIISqF4ZIW4oqHh9lleJgQQgghhIiNAruLeevSOZJTWO5zfbXpEFsPV/6ooIMnC/hs3X7sTjXqYzOyC5m3Lp1Chyvsfj9uO8K6vcfLGmKZGS9piZfhYUKI2EhLS2P27Nm69lUUhf/+978VGo8Q1Uk0rw8haoKZ323lvi82c9mcVeU6z8qdR7nzk984/4WfYxSZfqNm/cQ/v/idOT/t0rW/79qSl766kvu+2MysxTtC7n84u4BJ767jijmryxtq1IyXtMRJI74QQggh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Bwer5cuXu5SzKysrc7S5++67Vbt27dTSpUvVhg0bVHJyskpOTnacryllN3r0aLVlyxb166+/qoiICLel7B5++GG1e/duNXv27DMqG/jYY4+pFStWqIMHD6pt27apxx57TGmaphYuXOixMbvjXKHFU+N+6KGH1PLly9XBgwfVb7/9plJSUlR4eLjKycnx2JjXrVunDAaDeuGFF9S+ffvUZ599pvz9/dWnn37qaOOJr0el7JWl2rVrpx599NF65zzxuRanJn1T80nfJH3TqUjfJH2TO16TtCil1JtvvqnatWunjEajGjhwoFq7du15u+9ly5YpoN6/yZMnK6XspeyeeOIJFRUVpUwmkxo5cqRKS0tzucaJEyfUzTffrAIDA1VQUJCaOnWqKi4udmmzdetWNXToUGUymVSbNm3USy+9dNoxu4sXUHPmzHG0KS8vV3/9619VaGio8vf3V9dee606fvy4y3UOHTqkxo4dq/z8/FR4eLh66KGHlMViqff89O3bVxmNRtWhQweX+2iu22+/XcXHxyuj0agiIiLUyJEjHZ2Cp8bsTt2OwRPjvummm1RMTIwyGo2qTZs26qabbnKpKe+JMSul1I8//qh69uypTCaT6tatm3r33Xddznvi61EppRYsWKCAerEo5bnPtTg16ZuaR/om6ZtORfom6Zvc0ZRSqnljM0IIIYQQQghx/njFmhYhhBBCCCHExUuSFiGEEEIIIYRHk6RFCCGEEEII4dEkaRFCCCGEEEJ4NElahBBCCCGEEB5NkhYhhBBCCCGER5OkRQghhBBCCOHRJGkRQgghhBBCeDRJWoSoY8qUKUyYMKGlwxBCCCEcpG8SFztJWoQQQgghhBAeTZIWcdH65ptv6NWrF35+frRu3ZqUlBQefvhhPv74Y+bNm4emaWiaxvLlywE4cuQIf/zjHwkJCSEsLIxrrrmGQ4cOOa5X8ynYM888Q0REBEFBQdx9991UVla2zAMUQgjhdaRvEsI9Q0sHIERLOH78ODfffDMvv/wy1157LcXFxaxatYpJkyaRkZFBUVERc+bMASAsLAyLxcKYMWNITk5m1apVGAwGnn/+ea688kq2bduG0WgEYMmSJfj6+rJ8+XIOHTrE1KlTad26NS+88EJLPlwhhBBeQPomIRomSYu4KB0/fpyqqiquu+464uPjAejVqxcAfn5+mM1moqOjHe0//fRTbDYb77//PpqmATBnzhxCQkJYvnw5o0ePBsBoNPLhhx/i7+9Pjx49ePbZZ3n44Yd57rnn0OlkYFMIIUTDpG8SomHymyouSn369GHkyJH06tWLG2+8kffee4+TJ0822H7r1q3s37+fVq1aERgYSGBgIGFhYVRUVHDgwAGX6/r7+zu+T05OpqSkhCNHjpzTxyOEEML7Sd8kRMNkpEVclPR6PYsWLeL3339n4cKFvPnmm/zjH/8gNTXVbfuSkhL69+/PZ599Vu9cRETEuQ5XCCHERUD6JiEaJkmLuGhpmsaQIUMYMmQITz75JPHx8Xz//fcYjUasVqtL20suuYSvvvqKyMhIgoKCGrzm1q1bKS8vx8/PD4C1a9cSGBhIXFzcOX0sQgghLgzSNwnhnkwPExel1NRUXnzxRTZs2EBGRgbfffcdubm5dO/enYSEBLZt20ZaWhp5eXlYLBYmTpxIeHg411xzDatWreLgwYMsX76c++67j6NHjzquW1lZyR133MGuXbuYP38+Tz31FNOnT5c5w0IIIU5J+iYhGiYjLeKiFBQUxMqVK3n99dcpKioiPj6ef/7zn4wdO5akpCSWL19OUlISJSUlLFu2jMsuu4yVK1fy6KOPct1111FcXEybNm0YOXKky6dbI0eOpHPnzgwfPhyz2czNN9/M008/3XIPVAghhNeQvkmIhmlKKdXSQQhxIZgyZQoFBQXMnTu3pUMRQgghAOmbxIVDxgWFEEIIIYQQHk2SFiGEEEIIIYRHk+lhQgghhBBCCI8mIy1CCCGEEEIIjyZJixBCCCGEEMKjSdIihBBCCCGE8GiStAghhBBCCCE8miQtQgghhBBCCI8mSYsQQgghhBDCo0nSIoQQQgghhPBokrQIIYQQQgghPJokLUIIIYQQQgiP9v8BXL0FMtBMOocAAAAASUVORK5CYII=",
      "text/plain": [
       "<Figure size 1000x500 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "#画线要注意的是损失是不一定在零到1之间的\n",
    "def plot_learning_curves(record_dict, sample_step=500):\n",
    "    # build DataFrame\n",
    "    train_df = pd.DataFrame(record_dict[\"train\"]).set_index(\"step\").iloc[::sample_step]\n",
    "    val_df = pd.DataFrame(record_dict[\"val\"]).set_index(\"step\")\n",
    "\n",
    "    # plot\n",
    "    fig_num = len(train_df.columns)\n",
    "    fig, axs = plt.subplots(1, fig_num, figsize=(5 * fig_num, 5))\n",
    "    for idx, item in enumerate(train_df.columns):    \n",
    "        axs[idx].plot(train_df.index, train_df[item], label=f\"train_{item}\")\n",
    "        axs[idx].plot(val_df.index, val_df[item], label=f\"val_{item}\")\n",
    "        axs[idx].grid()\n",
    "        axs[idx].legend()\n",
    "        # axs[idx].set_xticks(range(0, train_df.index[-1], 5000))\n",
    "        # axs[idx].set_xticklabels(map(lambda x: f\"{int(x/1000)}k\", range(0, train_df.index[-1], 5000)))\n",
    "        axs[idx].set_xlabel(\"step\")\n",
    "    \n",
    "    plt.show()\n",
    "\n",
    "plot_learning_curves(record, sample_step=10)  #横坐标是 steps"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 评估"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "metadata": {
    "ExecutionIndicator": {
     "show": true
    },
    "execution": {
     "iopub.execute_input": "2025-02-02T16:23:49.719065Z",
     "iopub.status.busy": "2025-02-02T16:23:49.717902Z",
     "iopub.status.idle": "2025-02-02T16:23:51.379181Z",
     "shell.execute_reply": "2025-02-02T16:23:51.377739Z",
     "shell.execute_reply.started": "2025-02-02T16:23:49.719014Z"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "loss:     0.6757\n",
      "accuracy: 0.7712\n"
     ]
    }
   ],
   "source": [
    "# dataload for evaluating\n",
    "\n",
    "# load checkpoints\n",
    "model.load_state_dict(torch.load(f\"checkpoints/{exp_name}/best.ckpt\",weights_only=True, map_location=\"cpu\"))\n",
    "\n",
    "model.eval()\n",
    "loss, acc = evaluating(model, eval_dl, loss_fct)\n",
    "print(f\"loss:     {loss:.4f}\\naccuracy: {acc:.4f}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 推理"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2025-01-21T07:52:19.663752Z",
     "iopub.status.busy": "2025-01-21T07:52:19.663487Z",
     "iopub.status.idle": "2025-01-21T07:54:19.176801Z",
     "shell.execute_reply": "2025-01-21T07:54:19.176329Z",
     "shell.execute_reply.started": "2025-01-21T07:52:19.663728Z"
    }
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "100%|██████████| 2344/2344 [01:59<00:00, 19.62it/s]\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>filepath</th>\n",
       "      <th>class</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>competitions/cifar-10/test/1.png</td>\n",
       "      <td>deer</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>competitions/cifar-10/test/2.png</td>\n",
       "      <td>airplane</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>competitions/cifar-10/test/3.png</td>\n",
       "      <td>automobile</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>competitions/cifar-10/test/4.png</td>\n",
       "      <td>ship</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>competitions/cifar-10/test/5.png</td>\n",
       "      <td>airplane</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "                           filepath       class\n",
       "0  competitions/cifar-10/test/1.png        deer\n",
       "1  competitions/cifar-10/test/2.png    airplane\n",
       "2  competitions/cifar-10/test/3.png  automobile\n",
       "3  competitions/cifar-10/test/4.png        ship\n",
       "4  competitions/cifar-10/test/5.png    airplane"
      ]
     },
     "execution_count": 20,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# test_df\n",
    "test_ds = Cifar10Dataset(\"test\", transform=transforms_eval)\n",
    "test_dl = DataLoader(test_ds, batch_size=batch_size, shuffle=False, drop_last=False)\n",
    "\n",
    "preds_collect = []\n",
    "model.eval()\n",
    "for data, fake_label in tqdm(test_dl):\n",
    "    data = data.to(device=device)\n",
    "    logits = model(data)\n",
    "    preds = [test_ds.idx_to_label[idx] for idx in logits.argmax(axis=-1).cpu().tolist()]\n",
    "    preds_collect.extend(preds)\n",
    "    \n",
    "test_df[\"class\"] = preds_collect\n",
    "test_df.head()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2025-01-21T07:54:19.177784Z",
     "iopub.status.busy": "2025-01-21T07:54:19.177388Z",
     "iopub.status.idle": "2025-01-21T07:54:19.494510Z",
     "shell.execute_reply": "2025-01-21T07:54:19.493915Z",
     "shell.execute_reply.started": "2025-01-21T07:54:19.177764Z"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "# 导出 submission.csv\n",
    "test_df.to_csv(\"submission.csv\", index=False)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.14"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
